home *** CD-ROM | disk | FTP | other *** search
/ Die Speccy' 97 / Die Speccy' 97.iso / amiga_system / the_aminet / dev / lang / python020.lha / python / lib / rexec.py < prev    next >
Text File  |  1995-10-22  |  8KB  |  317 lines

  1. """Restricted execution facilities.
  2.  
  3. The class RExec exports methods rexec(), reval(), rexecfile(), and
  4. import_module(), which correspond roughly to the built-in operations
  5. exec, eval(), execfile() and import, but executing the code in an
  6. environment that only exposes those built-in operations that are
  7. deemed safe.  To this end, a modest collection of 'fake' modules is
  8. created which mimics the standard modules by the same names.  It is a
  9. policy decision which built-in modules and operations are made
  10. available; this module provides a reasonable default, but derived
  11. classes can change the policies e.g. by overriding or extending class
  12. variables like ok_builtin_modules or methods like make_sys().
  13.  
  14. XXX To do:
  15. - r_open should allow writing tmp dir
  16. - r_exec etc. with explicit globals/locals? (Use rexec("exec ... in ...")?)
  17. - r_reload should reload from same location (that's one for ihooks?)
  18.  
  19. """
  20.  
  21.  
  22. import sys
  23. import __builtin__
  24. import os
  25. import marshal
  26. import ihooks
  27.  
  28.  
  29. class FileBase:
  30.  
  31.     ok_file_methods = ('fileno', 'flush', 'isatty', 'read', 'readline',
  32.         'readlines', 'seek', 'tell', 'write', 'writelines')
  33.  
  34.  
  35. class FileWrapper(FileBase):
  36.  
  37.     def __init__(self, f):
  38.         self.f = f
  39.         for m in self.ok_file_methods:
  40.             if not hasattr(self, m):
  41.                 setattr(self, m, getattr(f, m))
  42.     
  43.     def close(f):
  44.         self.flush()
  45.  
  46.  
  47. TEMPLATE = """
  48. def %s(self, *args):
  49.     return apply(getattr(self.mod, self.name).%s, args)
  50. """
  51.  
  52. class FileDelegate(FileBase):
  53.  
  54.     def __init__(self, mod, name):
  55.         self.mod = mod
  56.         self.name = name
  57.     
  58.     for m in FileBase.ok_file_methods + ('close',):
  59.         exec TEMPLATE % (m, m)
  60.  
  61.  
  62. class RHooks(ihooks.Hooks):
  63.  
  64.     def __init__(self, rexec, verbose=0):
  65.     ihooks.Hooks.__init__(self, verbose)
  66.     self.rexec = rexec
  67.  
  68.     def is_builtin(self, name):
  69.     return self.rexec.is_builtin(name)
  70.  
  71.     def init_builtin(self, name):
  72.     m = __import__(name)
  73.     return self.rexec.copy_except(m, ())
  74.  
  75.     def init_frozen(self, name): raise SystemError, "don't use this"
  76.     def load_source(self, *args): raise SystemError, "don't use this"
  77.     def load_compiled(self, *args): raise SystemError, "don't use this"
  78.  
  79.     def load_dynamic(self, *args):
  80.     raise ImportError, "import of dynamically loaded modules not allowed"
  81.  
  82.     def add_module(self, name):
  83.     return self.rexec.add_module(name)
  84.  
  85.     def modules_dict(self):
  86.     return self.rexec.modules
  87.  
  88.     def default_path(self):
  89.     return self.rexec.modules['sys'].path
  90.  
  91.  
  92. class RModuleLoader(ihooks.FancyModuleLoader):
  93.  
  94.     def load_module(self, name, stuff):
  95.         file, filename, info = stuff
  96.         m = ihooks.FancyModuleLoader.load_module(self, name, stuff)
  97.         m.__filename__ = filename
  98.         return m
  99.  
  100.  
  101. class RModuleImporter(ihooks.ModuleImporter):
  102.  
  103.     def reload(self, module, path=None):
  104.         if path is None and hasattr(module, '__filename__'):
  105.         head, tail = os.path.split(module.__filename__)
  106.         path = [os.path.join(head, '')]
  107.         return ihooks.ModuleImporter.reload(self, module, path)
  108.  
  109.  
  110. class RExec(ihooks._Verbose):
  111.  
  112.     """Restricted Execution environment."""
  113.  
  114.     ok_path = tuple(sys.path)        # That's a policy decision
  115.  
  116.     ok_builtin_modules = ('array', 'binascii', 'audioop', 'imageop',
  117.               'marshal', 'math',
  118.               'md5', 'parser', 'regex', 'rotor', 'select',
  119.               'strop', 'struct', 'time')
  120.  
  121.     ok_posix_names = ('error', 'fstat', 'listdir', 'lstat', 'readlink',
  122.               'stat', 'times', 'uname', 'getpid', 'getppid',
  123.               'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')
  124.  
  125.     ok_sys_names = ('ps1', 'ps2', 'copyright', 'version',
  126.             'platform', 'exit', 'maxint')
  127.  
  128.     nok_builtin_names = ('open', 'reload', '__import__')
  129.  
  130.     def __init__(self, hooks = None, verbose = 0):
  131.     ihooks._Verbose.__init__(self, verbose)
  132.     # XXX There's a circular reference here:
  133.     self.hooks = hooks or RHooks(self, verbose)
  134.     self.modules = {}
  135.     self.ok_builtin_modules = map(None, filter(
  136.         lambda mname: mname in sys.builtin_module_names,
  137.         self.ok_builtin_modules))
  138.     self.make_builtin()
  139.     self.make_initial_modules()
  140.     # make_sys must be last because it adds the already created
  141.     # modules to its builtin_module_names
  142.     self.make_sys()
  143.     self.loader = RModuleLoader(self.hooks, verbose)
  144.     self.importer = RModuleImporter(self.loader, verbose)
  145.  
  146.     def make_initial_modules(self):
  147.     self.make_main()
  148.     self.make_osname()
  149.  
  150.     # Helpers for RHooks
  151.  
  152.     def is_builtin(self, mname):
  153.     return mname in self.ok_builtin_modules
  154.  
  155.     # The make_* methods create specific built-in modules
  156.  
  157.     def make_builtin(self):
  158.     m = self.copy_except(__builtin__, self.nok_builtin_names)
  159.     m.__import__ = self.r_import
  160.     m.reload = self.r_reload
  161.     m.open = self.r_open
  162.  
  163.     def make_main(self):
  164.     m = self.add_module('__main__')
  165.  
  166.     def make_osname(self):
  167.     osname = os.name
  168.     src = __import__(osname)
  169.     dst = self.copy_only(src, self.ok_posix_names)
  170.     dst.environ = e = {}
  171.     for key, value in os.environ.items():
  172.         e[key] = value
  173.  
  174.     def make_sys(self):
  175.     m = self.copy_only(sys, self.ok_sys_names)
  176.     m.modules = self.modules
  177.     m.argv = ['RESTRICTED']
  178.     m.path = map(None, self.ok_path)
  179.     m = self.modules['sys']
  180.     m.builtin_module_names = \
  181.         self.modules.keys() + self.ok_builtin_modules
  182.     m.builtin_module_names.sort()
  183.  
  184.     # The copy_* methods copy existing modules with some changes
  185.  
  186.     def copy_except(self, src, exceptions):
  187.     dst = self.copy_none(src)
  188.     for name in dir(src):
  189.         if name not in exceptions:
  190.         setattr(dst, name, getattr(src, name))
  191.     return dst
  192.  
  193.     def copy_only(self, src, names):
  194.     dst = self.copy_none(src)
  195.     for name in names:
  196.         try:
  197.         value = getattr(src, name)
  198.         except AttributeError:
  199.         continue
  200.         setattr(dst, name, value)
  201.     return dst
  202.  
  203.     def copy_none(self, src):
  204.     return self.add_module(src.__name__)
  205.  
  206.     # Add a module -- return an existing module or create one
  207.  
  208.     def add_module(self, mname):
  209.     if self.modules.has_key(mname):
  210.         return self.modules[mname]
  211.     self.modules[mname] = m = self.hooks.new_module(mname)
  212.     m.__builtins__ = self.modules['__builtin__']
  213.     return m
  214.  
  215.     # The r* methods are public interfaces
  216.  
  217.     def r_exec(self, code):
  218.     m = self.add_module('__main__')
  219.     exec code in m.__dict__
  220.  
  221.     def r_eval(self, code):
  222.     m = self.add_module('__main__')
  223.     return eval(code, m.__dict__)
  224.  
  225.     def r_execfile(self, file):
  226.     m = self.add_module('__main__')
  227.     return execfile(file, m.__dict__)
  228.  
  229.     def r_import(self, mname, globals={}, locals={}, fromlist=[]):
  230.     return self.importer.import_module(mname, globals, locals, fromlist)
  231.  
  232.     def r_reload(self, m):
  233.         return self.importer.reload(m)
  234.  
  235.     def r_unload(self, m):
  236.         return self.importer.unload(m)
  237.     
  238.     # The s_* methods are similar but also swap std{in,out,err}
  239.  
  240.     def set_files(self):
  241.         s = self.modules['sys']
  242.         s.stdin = FileWrapper(sys.stdin)
  243.         s.stdout = FileWrapper(sys.stdout)
  244.         s.stderr = FileWrapper(sys.stderr)
  245.     sys.stdin = FileDelegate(s, 'stdin')
  246.     sys.stdout = FileDelegate(s, 'stdout')
  247.     sys.stderr = FileDelegate(s, 'stderr')
  248.  
  249.     def save_files(self):
  250.         self.save_stdin = sys.stdin
  251.         self.save_stdout = sys.stdout
  252.         self.save_stderr = sys.stderr
  253.  
  254.     def restore_files(files):
  255.     sys.stdin = self.save_sydin
  256.     sys.stdout = self.save_stdout
  257.     sys.stderr = self.save_stderr
  258.     
  259.     def s_apply(self, func, *args, **kw):
  260.     self.save_files()
  261.     try:
  262.         self.set_files()
  263.         r = apply(func, args, kw)
  264.         finally:
  265.         self.restore_files()
  266.     
  267.     def s_exec(self, *args):
  268.         self.s_apply(self.r_exec, args)
  269.     
  270.     def s_eval(self, *args):
  271.         self.s_apply(self.r_eval, args)
  272.     
  273.     def s_execfile(self, *args):
  274.         self.s_apply(self.r_execfile, args)
  275.     
  276.     def s_import(self, *args):
  277.         self.s_apply(self.r_import, args)
  278.     
  279.     def s_reload(self, *args):
  280.         self.s_apply(self.r_reload, args)
  281.     
  282.     def s_unload(self, *args):
  283.         self.s_apply(self.r_unload, args)
  284.     
  285.     # Restricted open(...)
  286.     
  287.     def r_open(self, file, mode='r', buf=-1):
  288.         if mode not in ('r', 'rb'):
  289.             raise IOError, "can't open files for writing in restricted mode"
  290.         return open(file, mode, buf)
  291.  
  292.  
  293. def test():
  294.     import traceback
  295.     r = RExec(None, '-v' in sys.argv[1:])
  296.     print "*** RESTRICTED *** Python", sys.version
  297.     print sys.copyright
  298.     while 1:
  299.     try:
  300.         try:
  301.         s = raw_input('>>> ')
  302.         except EOFError:
  303.         print
  304.         break
  305.         if s and s[0] != '#':
  306.         s = s + '\n'
  307.         c = compile(s, '<stdin>', 'single')
  308.         r.r_exec(c)
  309.     except SystemExit, n:
  310.         sys.exit(n)
  311.     except:
  312.         traceback.print_exc()
  313.  
  314.  
  315. if __name__ == '__main__':
  316.     test()
  317.